package com.example.exceldemo.utils;

import com.example.exceldemo.exception.ServiceException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
import org.springframework.util.CollectionUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.ServerException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author xiaohuang
 * @Date 2025/1/7
 */
public class PoiUtils {
    /**
     * 创建文档标题
     *
     * @param doc     文档对象，用于添加标题
     * @param content 标题的内容
     */
    public static void createTitle(XWPFDocument doc, String content) {
        // 创建一个段落对象，并设置其对齐方式为居中
        XWPFParagraph title = doc.createParagraph();
        title.setAlignment(ParagraphAlignment.CENTER);
        // 在标题段落中添加文本，设置为粗体、宋体、字号为22
        XWPFRun r1 = title.createRun();
        r1.setBold(true);
        r1.setFontFamily("宋体");
        r1.setText(content);
        r1.setFontSize(22);
    }

    /**
     * 创建文档的第一级章节标题
     *
     * @param doc     文档对象，用于添加章节标题
     * @param content 章节标题的内容
     */
    public static void createChapterH1(XWPFDocument doc, String content) {
        // 创建一个段落对象，并设置其对齐方式为左对齐
        XWPFParagraph actTheme = doc.createParagraph();
        actTheme.setAlignment(ParagraphAlignment.LEFT);
        // 在章节标题段落中添加文本，设置为粗体、字号为18
        XWPFRun runText1 = actTheme.createRun();
        runText1.setBold(true);
        runText1.setText(content);
        runText1.setFontSize(18);
    }

    /**
     * 创建文档的第二级章节标题
     *
     * @param doc     文档对象，用于添加章节标题
     * @param content 章节标题的内容
     */
    public static void createChapterH2(XWPFDocument doc, String content) {
        // 创建一个段落对象
        XWPFParagraph actType = doc.createParagraph();
        // 在章节标题段落中添加文本，设置为粗体、字号为15
        XWPFRun runText2 = actType.createRun();
        runText2.setBold(true);
        runText2.setText(content);
        runText2.setFontSize(15);
    }

    /**
     * 创建文档的普通段落
     *
     * @param doc     文档对象，用于添加段落
     * @param content 段落的内容
     */
    public static void createParagraph(XWPFDocument doc, String content) {
        // 创建一个段落对象
        XWPFParagraph actType = doc.createParagraph();
        // 在段落中添加文本，设置字号为11
        XWPFRun runText2 = actType.createRun();
        runText2.setText(content);
        runText2.setFontSize(11);
    }

    /**
     * 获取Workbook对象
     */
    public static Workbook getWorkBook(InputStream is) {
        //这样写excel能兼容03和07
        Workbook hssfWorkbook = null;
        try {
            hssfWorkbook = new HSSFWorkbook(is);
        } catch (Exception ex) {
            try {
                hssfWorkbook = new XSSFWorkbook(is);
            } catch (IOException e) {
                throw new ServiceException("XSSFWorkbook获取Workbook异常", e);
            }
        }
        return hssfWorkbook;
    }

    /**
     * 批量获取Excel2003图片,只能读取浮动
     * 此方法用于从指定的工作表中提取所有图片数据，并将其按照图片在工作表中的位置进行分类
     *
     * @param sheetNum 工作表编号，用于标识特定的工作表
     * @param sheet    HSSFSheet对象，代表要处理的工作表
     * @param workbook HSSFWorkbook对象，代表整个Excel工作簿
     * @return 返回一个映射，其中键是图片的位置（行号和列号），值是位于该位置的图片数据列表如果工作表中没有图片，则返回null
     */
    public static Map<String, List<PictureData>> getSheetPicturesBatch03(Integer sheetNum,
                                                                         HSSFSheet sheet, HSSFWorkbook workbook) {
        // 创建一个映射，用于存储图片数据，键为图片坐标，值为图片数据列表
        Map<String, List<PictureData>> sheetIndexPicMap = new HashMap();
        // 获取工作簿中的所有图片
        List<HSSFPictureData> pictures = workbook.getAllPictures();
        // 如果图片列表不为空
        if (pictures.size() != 0) {
            // 获取工作表中的所有形状
            List<HSSFShape> shapeList = sheet.getDrawingPatriarch().getChildren();
            // 如果形状列表不为空
            if (!CollectionUtils.isEmpty(shapeList)) {
                // 过滤出图片形状
                shapeList = shapeList.stream().filter(item -> item instanceof HSSFPicture).collect(Collectors.toList());
                // 遍历图片形状
                for (HSSFShape shape : shapeList) {
                    HSSFPicture hssfPicture = (HSSFPicture) shape;
                    HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
                    //获取图片坐标
                    String picIndex = "";
                    if (sheetNum == null)
                        picIndex = anchor.getRow1() + "_" + anchor.getCol1();
                    else
                        picIndex = sheetNum + "_" + anchor.getRow1() + "_" + anchor.getCol1();
                    //获取图片数据
                    HSSFPictureData pictureData = hssfPicture.getPictureData();
                    //如果存在这个坐标KEY表示相同单元格中的图片,直接集合添加该图片,不存在该坐标key直接创建添加
                    if (sheetIndexPicMap.containsKey(picIndex)) {
                        sheetIndexPicMap.get(picIndex).add(pictureData);
                    } else {
                        List<PictureData> arrayList = new ArrayList<>();
                        arrayList.add(pictureData);
                        sheetIndexPicMap.put(picIndex, arrayList);
                    }
                }

            }
        }
        return sheetIndexPicMap;
    }


    /**
     * 获取Excel2007图片，只能读取浮动，嵌入图片不支持
     *
     * @param sheetNum 当前sheet编号
     * @param sheet    当前sheet对象
     * @param workbook 工作簿对象
     * @return Map key:图片单元格索引（0_1_1）String，value:图片流PictureData
     */
    public static Map<String, List<PictureData>> getSheetPicturesBatch07(Integer sheetNum, XSSFSheet sheet, XSSFWorkbook workbook) {
        // 创建一个映射，用于存储图片数据，键为图片坐标，值为图片数据列表
        Map<String, List<PictureData>> sheetIndexPicMap = new HashMap();
        // 遍历工作表中的所有关系
        for (POIXMLDocumentPart dr : sheet.getRelations()) {
            // 如果关系是绘图类型
            if (dr instanceof XSSFDrawing) {
                XSSFDrawing drawing = (XSSFDrawing) dr;
                List<XSSFShape> shapes = drawing.getShapes();
                // 遍历形状
                for (XSSFShape shape : shapes) {
                    XSSFPicture pic = (XSSFPicture) shape;
                    XSSFClientAnchor anchor = pic.getPreferredSize();
                    //获取坐标
                    CTMarker ctMarker = anchor.getFrom();
                    String picIndex = "";
                    if (sheetNum == null)
                        picIndex = ctMarker.getRow() + "_" + ctMarker.getCol();
                    else
                        picIndex = sheetNum + "_" + ctMarker.getRow() + "_" + ctMarker.getCol();
                    //获取图片
                    XSSFPictureData data = pic.getPictureData();
                    //如果存在这个坐标KEY表示相同单元格中的图片,直接集合添加该图片,不存在该坐标key直接创建添加
                    if (sheetIndexPicMap.containsKey(picIndex))
                        sheetIndexPicMap.get(picIndex).add(data);
                    else {
                        List<PictureData> pictureData = new ArrayList<>();
                        pictureData.add(data);
                        sheetIndexPicMap.put(picIndex, pictureData);
                    }
                }
            }
        }
        return sheetIndexPicMap;
    }

    /**
     * 批量获取Excel2007浮动图片
     *
     * @param sheetNum 当前sheet编号
     * @param sheet    当前sheet对象
     * @param workbook 工作簿对象
     * @return Map key:图片单元格索引（0_1_1）String，value:图片流PictureData
     */
    public static Map<String, List<PictureData>> getFlowPicturesBatch07(Integer sheetNum, XSSFSheet sheet, XSSFWorkbook workbook) {
        // 创建一个映射，用于存储图片数据，键为图片坐标，值为图片数据列表
        Map<String, List<PictureData>> sheetIndexPicMap = new HashMap();
        // 获取工作表中的所有关系
        List<POIXMLDocumentPart> list = sheet.getRelations();
        // 如果关系列表不为空
        if (!CollectionUtils.isEmpty(list)) {
            // 遍历关系
            for (POIXMLDocumentPart part : list) {
                // 如果关系是绘图类型
                if (part instanceof XSSFDrawing) {
                    //获取工作表中的绘图
                    XSSFDrawing drawing = (XSSFDrawing) part;
                    if (drawing != null) {
                        //获取所有图像形状
                        List<XSSFShape> shapes = drawing.getShapes();
                        // 遍历形状
                        for (XSSFShape shape : shapes) {
                            //获取形状在工作表中的质点位置信息(anchor描点)
                            XSSFClientAnchor anchor = (XSSFClientAnchor) shape.getAnchor();
                            // 如果形状是图片类型
                            if (shape instanceof XSSFPicture) {
                                //形状获取对应的图片
                                XSSFPicture pic = (XSSFPicture) shape;
                                XSSFPictureData data = pic.getPictureData();
                                //图片形状在工作表中的位置, 所在行列起点和终点位置
                                //列
                                short c1 = anchor.getCol1();
                                //行
                                int r1 = anchor.getRow1();
                                String picIndex = "";
                                if (sheetNum == null)
                                    picIndex = r1 + "_" + c1;
                                else
                                    picIndex = sheetNum + "_" + r1 + "_" + c1;
                                //如果存在这个坐标KEY表示相同单元格中的图片,直接集合添加该图片,不存在该坐标key直接创建添加
                                if (sheetIndexPicMap.containsKey(picIndex))
                                    sheetIndexPicMap.get(picIndex).add(data);
                                else {
                                    List<PictureData> pictureData = new ArrayList<>();
                                    pictureData.add(data);
                                    sheetIndexPicMap.put(picIndex, pictureData);
                                }
                            }
                        }

                    }

                }

            }

        }
        return sheetIndexPicMap;
    }
    /**
     * 获取Excel文件中所有Sheet的名称
     *
     * @param fileBytes 文件内容的字节数组
     * @return Sheet名称列表
     * @throws IOException 如果读取文件失败
     */
    public static List<String> getSheetNames(byte[] fileBytes) throws IOException {
        List<String> sheetNames = new ArrayList<>();
        try (InputStream stream = new ByteArrayInputStream(fileBytes);
             Workbook workbook = WorkbookFactory.create(stream)) {
            int sheetCount = workbook.getNumberOfSheets();
            for (int i = 0; i < sheetCount; i++) {
                String name = workbook.getSheetName(i);
                sheetNames.add(name != null ? name.trim() : "");
            }
        } catch (InvalidFormatException e) {
            throw new ServerException("无效的文件格式", e);
        }
        return sheetNames;
    }
}
